package com.github.xtranslation.core.service;


import cn.hutool.core.collection.CollUtil;
import com.github.xtranslation.core.core.TransFieldMeta;
import com.github.xtranslation.core.core.TransModel;
import com.github.xtranslation.core.manager.TransClassMetaCacheManager;
import com.github.xtranslation.core.repository.TransRepository;
import com.github.xtranslation.core.repository.TransRepositoryFactory;
import com.github.xtranslation.core.resolver.WrapperObjectResolver;
import com.github.xtranslation.core.resolver.TransObjResolverFactory;
import com.github.xtranslation.core.threadpool.DefaultExecutor;
import com.github.xtranslation.core.util.CollectionUtils;
import io.vavr.Tuple;
import io.vavr.control.Option;
import lombok.Setter;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

import static io.vavr.API.*;


/**
 * TransService: 翻译服务类,用于将对象中的字段值翻译成其他格式。
 * <p>
 * 翻译框架的核心服务组件,负责整个翻译流程的协调和执行。
 * 提供对象翻译功能,支持复杂对象、集合对象和嵌套对象的翻译。
 * </p>
 * <p>
 * 核心职责：
 * 1. 对象解析：处理包装对象,提取需要翻译的目标对象
 * 2. 元数据管理：获取和缓存对象的翻译元数据信息
 * 3. 翻译执行：协调各个组件完成实际的翻译操作
 * 4. 并发处理：支持多线程并发翻译,提高处理效率
 * </p>
 *
 * @author zhangxiaoxiang
 * @see com.github.xtranslation.core.core.TransModel
 * @see com.github.xtranslation.core.manager.TransClassMetaCacheManager
 * @see com.github.xtranslation.core.repository.TransRepository
 * @see WrapperObjectResolver
 * @since 2025/7/27
 */
public class TransService {

    /**
     * 线程池,用于并发执行翻译任务
     * <p>
     * 可通过setter方法自定义线程池实现,若未设置则使用默认的缓存线程池。
     * 线程名称格式为"trans-thread-"加上任务对象的hashCode值。
     * </p>
     */
    @Setter
    private ExecutorService executor;

    /**
     * 初始化状态标识
     * <p>
     * 使用volatile关键字确保多线程环境下的可见性。
     * true表示服务已初始化完成,可以正常处理翻译请求。
     * </p>
     */
    private volatile boolean isInit = false;

    /**
     * 初始化方法
     * <p>
     * 完成翻译服务的初始化工作,主要包括：
     * 1. 检查并创建线程池
     * 2. 设置初始化完成标识
     * </p>
     * <p>
     * 注意事项：
     * 1. 必须在使用翻译功能前调用此方法
     * 2. 可重复调用,但不会重新初始化
     * </p>
     */
    public void init() {
        // Option.of(this.executor).onEmpty(() -> this.executor = Executors.newCachedThreadPool(r -> new Thread(r, "trans-thread-" + r.hashCode())));
        Option.of(this.executor).onEmpty(() -> this.executor = DefaultExecutor.getExecutor());
        // 这个方法会将isInit设置为true,表示TransService已经初始化完成。
        isInit = true;
    }

    /**
     * 执行对象翻译
     * <p>
     * 翻译服务的核心入口方法,负责协调整个翻译流程。
     * 支持单个对象和集合对象的翻译。
     * </p>
     * <p>
     * 处理流程：
     * 1. 检查服务是否已初始化
     * 2. 解析包装对象
     * 3. 获取翻译元数据
     * 4. 构建翻译模型
     * 5. 执行实际翻译
     * </p>
     *
     * @param obj 需要被翻译的对象,可以是单个对象或集合对象
     * @return 是否翻译成功,true表示成功,false表示失败或无需翻译
     */
    public boolean trans(Object obj) {
        // 检查线程池是否准备好,并且对象解析后不为空
        return Option.of(obj)
                .filter(o -> isInit)
                .map(this::unwrapDataObject)
                .filter(Objects::nonNull)
                .map(CollectionUtils::objToList)
                .filter(CollUtil::isNotEmpty)
                // 排除几乎不可能翻译的类型,不是Java内置类,例如String,Integer等
                .filter(list -> !list.get(0).getClass().getName().startsWith("java."))
                // 获取元数据信息(因为list装的类的信息是一致的,说以直接取第一个作为类的元数据)
                .map(list -> Tuple.of(list, TransClassMetaCacheManager.getTransClassMeta(list.get(0).getClass())))
                .filter(tuple -> tuple._2.needTrans())
                // 执行翻译赋值的核心方法
                .map(tuple -> this.performTransByRepository(tuple._1, tuple._2.getTransFieldList()))
                .getOrElse(false);
    }

    /**
     * 递归解析包装对象,获取原始data(一般就是controller层封装的Result(code/msg/data)对象)
     * <p>
     * 通过TransObjResolverFactory获取已注册的解析器,
     * 找到第一个支持该对象的解析器进行解析。
     * 支持递归解析嵌套的包装对象。
     * </p>
     *
     * @param wrappedObject 需要解析的对象
     * @return 解析后的对象,如果对象无法解析或为空,则返回原对象
     */
    private Object unwrapDataObject(Object wrappedObject) {
        // 使用Option处理null值检查
        return Option.of(wrappedObject)
                .map(o -> {
                    // 通过EasyTransRegister取到使用方的包装类(可以是多个,例如控制层包装器,service层包装器等)
                    List<WrapperObjectResolver> resolvers = TransObjResolverFactory.getResolvers();

                    // 查找第一个支持该对象的解析器并处理
                    return resolvers.stream()
                            .filter(resolver -> resolver.needResolve(o))
                            .findFirst()
                            .map(resolver -> {
                                // 解析对象,把包装类的data取出来,例如Result<T>中的data字段值
                                Object resolvedObj = resolver.unwrapTargetObject(o);
                                // 递归处理嵌套翻译(因为Result<T>中的data字段值可能还是一个包装类,例如PageResult<UserVO>)
                                return this.unwrapDataObject(resolvedObj);
                            })
                            // 如果没有找到支持的解析器,返回原对象
                            .orElse(o);
                })
                .getOrNull();
    }

    /**
     * 执行转换操作
     * <p>
     * 根据翻译仓库对翻译字段进行分组,然后分别处理各组翻译任务。
     * 当存在多个翻译仓库时,使用并发方式提高处理效率。
     * </p>
     *
     * @param needTransVOList    需要转换的VO对象列表
     * @param transFieldMetaList 对应翻译元字段列表
     * @return 转换是否成功
     */
    private boolean performTransByRepository(List<Object> needTransVOList, List<TransFieldMeta> transFieldMetaList) {
        // 将转换字段信息按仓库类分组
        Map<? extends Class<? extends TransRepository>, List<TransFieldMeta>> listMap = transFieldMetaList.stream().collect(Collectors.groupingBy(TransFieldMeta::getRepository));

        // 考虑到新能,使用分类分类讨论思想,单个就正常执行,减少线程开支,多个就并行执行 哈哈哈
        return Match(listMap.size() > 1).of(
                Case($(true), () -> {
                    CompletableFuture.allOf(listMap.entrySet().stream().map(entry -> CompletableFuture.runAsync(() ->
                            this.transByRepository(needTransVOList, entry.getKey(), entry.getValue()), executor)).toArray(CompletableFuture[]::new)).join();
                    return true;
                }),
                Case($(), () -> {
                    listMap.forEach((transClass, transFields) -> this.transByRepository(needTransVOList, transClass, transFields));
                    return true;
                })
        );
    }


    /**
     * 执行转换操作
     * <p>
     * 处理特定翻译仓库的翻译任务,包括获取翻译数据和设置翻译值。
     * 同时递归处理嵌套的翻译字段。
     * </p>
     *
     * @param needTransVOList    需要转换的VO对象列表
     * @param transClass         转换仓库类
     * @param transFieldMetaList 对应翻译元字段列表
     */
    private void transByRepository(List<Object> needTransVOList, Class<? extends TransRepository> transClass, List<TransFieldMeta> transFieldMetaList) {
        Option.of(TransRepositoryFactory.getTransRepository(transClass))
                .forEach(transRepository -> {
                    // 获取需要被翻译的集合Map<trans, List<TransModel> >
                    Map<String, List<TransModel>> transMap = this.getTransMap(needTransVOList, transFieldMetaList);

                    // 使用Option处理transMap非空情况
                    Option.of(transMap).filter(CollUtil::isNotEmpty).forEach(map -> this.performSingleTrans(transRepository, map));

                    // 有嵌套属性,就继续翻译
                    transFieldMetaList.forEach(transField ->
                            Option.of(transField.getChildren()).filter(CollUtil::isNotEmpty)
                                    .forEach(children -> this.performTransByRepository(needTransVOList, children))
                    );
                });
    }

    /**
     * 获取需要翻译的集合
     * <p>
     * 根据需要翻译的对象列表和翻译字段元数据列表,
     * 构建翻译模型并按翻译字段进行分组。
     * </p>
     *
     * @param needTransVOList    需要被翻译的对象集合
     * @param transFieldMetaList 需要被翻译的属性
     * @return 需要被翻译的集合Map<trans, List < TransModel>>
     */
    private Map<String, List<TransModel>> getTransMap(List<Object> needTransVOList, List<TransFieldMeta> transFieldMetaList) {
        // 将toTransList中的每个TransFieldMeta对象与objList中的每个对象进行映射,生成TransModel对象
        return transFieldMetaList.stream()
                // 对每个TransFieldMeta对象,将其与objList中的每个对象进行映射,生成TransModel对象
                .flatMap(x -> needTransVOList.stream().map(o -> new TransModel(o, x)))
                // 过滤出需要翻译的TransModel对象
                .filter(TransModel::needTrans)
                // 根据TransFieldMeta对象的trans属性对TransModel对象进行分组
                .collect(Collectors.groupingBy(x -> x.getTransField().getTrans()));
    }


    /**
     * 执行转换操作（具体实现）
     * <p>
     * 把需要翻译的在总数据数据仓库仅需对比,对需要翻译的仅需赋值翻译。
     * 当存在多个翻译组时,使用并发方式提高处理效率。
     * </p>
     *
     * @param transRepository 转换仓库
     * @param transMap        需要转换的模型映射,键为转换标识,值为模型列表
     */
    private void performSingleTrans(TransRepository transRepository, Map<String, List<TransModel>> transMap) {
        // 分组查询
        Match(transMap.size() > 1).of(
                Case($(true), () -> {
                    CompletableFuture<?>[] futures = transMap.values()
                            .stream()
                            .map(transModels -> CompletableFuture.runAsync(() -> this.performTransByRepository(transRepository, transModels), executor))
                            .toArray(CompletableFuture[]::new);
                    CompletableFuture.allOf(futures).join();
                    return null; // Void方法需要返回null
                }),
                Case($(), () -> {
                    transMap.values().forEach(transModels -> this.performTransByRepository(transRepository, transModels));
                    return null; // Void方法需要返回null
                })
        );
    }

    /**
     * 执行转换操作
     * <p>
     * 调用翻译仓库获取翻译数据,并将翻译结果设置到翻译模型中。
     * 是翻译流程的最终执行环节。
     * </p>
     *
     * @param transRepository 转换仓库
     * @param transModels     包含转换模型的列表
     */
    private void performTransByRepository(TransRepository transRepository, List<TransModel> transModels) {
        // 获取所有转换模型中需要转换的值,去重后存入List
        List<Object> transIdList = transModels
                .stream()
                .map(TransModel::getMultipleTransVal)
                .flatMap(Collection::stream)
                .distinct()
                .collect(Collectors.toList());

        // 获取转换注解
        // 它们都属于同一个字段（同一个 TransFieldMeta）
        // 它们都使用相同的翻译仓库（TransRepository）
        // 它们都使用相同的翻译注解（@Trans 或相关注解）
        Annotation transAnno = transModels.get(0).getTransField().getTransAnno();

        // 获取转换值映射(使用者提供的数据源) userId -> userDO(数据库实体的对象),相当于根据id获取到id map
        Map<Object, Object> valueMap = transRepository.getTransValueMap(transIdList, transAnno);

        // 如果转换值映射不为空(遍历转换模型,设置转换后的值)
        Option.of(valueMap).filter(CollUtil::isNotEmpty)
                .peek(map -> transModels.forEach(transModel -> transModel.setValue(map)));

    }


}
